# steamcharts_client.py
import requests
import logging
import datetime
import json
from typing import List, Tuple, Optional, Dict, Any
import time
import matplotlib
import matplotlib.pyplot as plt

matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')


class SteamChartsClient:
    """ 
    负责与SteamCharts网站交互，获取游戏在线人数历史数据。
    """
    BASE_URL = "https://steamcharts.com/app/"
    DEFAULT_HEADERS = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
        "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept": "application/json, text/javascript, */*; q=0.01",
        "X-Requested-With": "XMLHttpRequest"
    }

    def __init__(self):
        logging.info("SteamChartsClient initialized.")
        self.headers = self.DEFAULT_HEADERS.copy()
        # 缓存所有数据，避免重复请求
        self._all_player_data_cache: Dict[int, List[Tuple[datetime.datetime, int]]] = {}

    def _make_request(self, app_id: int) -> Optional[List[List[int]]]:
        """
        内部方法：发送HTTP GET请求到SteamCharts的chart-data.json接口。
        此方法始终获取所有历史数据。
        Args:
            app_id (int): Steam游戏的AppID。
        Returns:
            Optional[List[List[int]]]: 原始的JSON数据 (二维数组)，如果请求失败则返回None。
        """
        url = f"{self.BASE_URL}{app_id}/chart-data.json"
        self.headers["Referer"] = f"https://steamcharts.com/app/{app_id}/"

        try:
            logging.debug(f"Requesting SteamCharts JSON from: {url} with headers: {self.headers}")
            response = requests.get(url, headers=self.headers, timeout=20)

            if response.status_code == 404:
                logging.warning(
                    f"SteamCharts API for AppID {app_id} returned 404 Not Found. This AppID might not exist or have chart data.")
                return None

            response.raise_for_status()  # 抛出HTTPError for other bad responses (4xx or 5xx)

            data = response.json()

            if isinstance(data, list) and all(isinstance(item, list) and len(item) == 2 for item in data):
                logging.debug(f"Successfully parsed JSON for AppID {app_id}. Data points: {len(data)}.")
                return data
            else:
                logging.error(
                    f"SteamCharts API for AppID {app_id} returned unexpected data format. Raw data (partial): {str(data)[:200]}")
                return None

        except requests.exceptions.HTTPError as e:
            logging.error(
                f"HTTP error occurred for SteamCharts AppID {app_id} ({url}): {e.response.status_code} - {e.response.text.strip()[:200]}")
            return None
        except requests.exceptions.ConnectionError as e:
            logging.error(f"Connection error occurred for SteamCharts AppID {app_id} ({url}): {e}")
            return None
        except requests.exceptions.Timeout as e:
            logging.error(f"Request timed out for SteamCharts AppID {app_id} ({url}): {e}")
            return None
        except json.JSONDecodeError as e:
            logging.error(f"Failed to decode JSON from SteamCharts for AppID {app_id} ({url}): {e}")
            if response is not None:
                logging.debug(f"Raw response text start: {response.text[:500]}...")
            return None
        except requests.exceptions.RequestException as e:
            logging.error(f"An unexpected request error occurred for SteamCharts AppID {app_id} ({url}): {e}")
            return None

    def get_player_count_data(self, app_id: int) -> Optional[List[Tuple[datetime.datetime, int]]]:
        """
        获取指定游戏的在线人数历史数据（所有时间范围），并转换为可用的 (datetime, players) 格式。
        数据会进行缓存以避免重复请求。
        Args:
            app_id (int): Steam游戏的AppID。
        Returns:
            Optional[List[Tuple[datetime.datetime, int]]]: 历史在线人数数据列表，每个元素为 (日期时间对象, 玩家数)。
                                                        如果获取失败或无数据，则返回None。
        """
        if app_id in self._all_player_data_cache:
            logging.info(f"Returning cached player count data for AppID: {app_id}")
            return self._all_player_data_cache[app_id]

        raw_data = self._make_request(app_id)
        if not raw_data:
            logging.warning(f"No raw player count data retrieved for AppID: {app_id}")
            return None

        processed_data: List[Tuple[datetime.datetime, int]] = []
        for entry in raw_data:
            try:
                timestamp_ms = entry[0]
                players = entry[1]

                dt_object = datetime.datetime.fromtimestamp(timestamp_ms / 1000)
                processed_data.append((dt_object, players))
            except (IndexError, TypeError, ValueError) as e:
                logging.error(f"Error processing player count entry for AppID {app_id}: {entry}. Error: {e}")
                continue

        processed_data.sort(key=lambda x: x[0])

        self._all_player_data_cache[app_id] = processed_data
        logging.info(
            f"Successfully processed and cached {len(processed_data)} player count data points for AppID: {app_id}")
        return processed_data
